﻿using System;

namespace EnterpriseLibraryExtensions.Services.Mapping
{
    /// <summary>
    /// The class is the base class to use when implementing entity translators.
    /// The class implements the <see cref="IEntityTranslator"/> interface but simplifies implementing the interface in a type safe manner.
    /// </summary>
    /// <typeparam name="TOneType">The one type in the mapping.</typeparam>
    /// <typeparam name="TAnotherType">The other type in the mapping.</typeparam>
    public abstract class EntityMapperTranslator<TOneType, TAnotherType> : IEntityTranslator
    {
        /// <summary>
        /// Returns if the implementation supports translating the two types.
        /// </summary>
        /// <typeparam name="TTarget">Type to translate to.</typeparam>
        /// <typeparam name="TSource">Type to translate from.</typeparam>
        /// <returns>If the instance support translating the two types.</returns>
        public bool CanTranslate<TTarget, TSource>()
        {
            return CanTranslate(typeof(TTarget), typeof(TSource));
        }

        /// <summary>
        /// Returns if the implementation supports translating the two types.
        /// </summary>
        /// <param name="targetType">Type to translate to.</param>
        /// <param name="sourceType">Type to translate from.</param>
        /// <returns>If the instance support translating the two types.</returns>
        public bool CanTranslate(Type targetType, Type sourceType)
        {
            return (targetType == typeof(TOneType) && sourceType == typeof(TAnotherType)) ||
                   (targetType == typeof(TAnotherType) && sourceType == typeof(TOneType));
        }

        /// <summary>
        /// Translates the source entity into an instance of the target type if the implementations supports it.
        /// </summary>
        /// <typeparam name="TTarget">The desired type of the target instance.</typeparam>
        /// <param name="service">The implementaiton if <see cref="IEntityTranslatorService"/> which the translator is registered to.</param>
        /// <param name="source">The instance to translate from.</param>
        /// <returns>The new instance.</returns>
        /// <exception cref="System.InvalidOperationException">Thrown when translator does not support the desired mapping.</exception>
        public TTarget Translate<TTarget>(IEntityTranslatorService service, object source)
        {
            return (TTarget)Translate(service, typeof(TTarget), source);
        }

        /// <summary>
        /// Translates the source entity into an instance of the target type if the implementations supports it.
        /// </summary>
        /// <param name="service">The implementaiton if <see cref="IEntityTranslatorService"/> which the translator is registered to.</param>
        /// <param name="targetType">The desired type of the target instance.</param>
        /// <param name="source">The instance to translate from.</param>
        /// <returns>The new instance.</returns>
        /// <exception cref="System.InvalidOperationException">Thrown when translator does not support the desired mapping.</exception>
        public object Translate(IEntityTranslatorService service, Type targetType, object source)
        {
            if (targetType == typeof(TOneType))
            {
                return ToOneType(service, (TAnotherType)source);
            }

            if (targetType == typeof(TAnotherType))
            {
                return ToAnotherType(service, (TOneType)source);
            }

            throw new InvalidOperationException("Mapping between " + source.GetType().Name + " and " + targetType.Name + " is not supported");
        }

        /// <summary>
        /// Translate from an instance of type TOneType to TAnotherType.
        /// </summary>
        /// <param name="service">The implementaiton if <see cref="IEntityTranslatorService"/> which the translator is registered to.</param>
        /// <param name="value">The instance to translate from.</param>
        /// <returns>The derived class must return an instance of type TAnotherType.</returns>
        protected abstract TAnotherType ToAnotherType(IEntityTranslatorService service, TOneType value);

        /// <summary>
        /// Translate from an instance of type TAnotherType to TOneType.
        /// </summary>
        /// <param name="service">The implementaiton if <see cref="IEntityTranslatorService"/> which the translator is registered to.</param>
        /// <param name="value">The instance to translate from.</param>
        /// <returns>The derived class must return an instance of type TOneType.</returns>
        protected abstract TOneType ToOneType(IEntityTranslatorService service, TAnotherType value);
    }

}
